/***********************************************************************
Vczh Library++ 3.0
Developer: Zihan Chen(vczh)
Data Structure::Smart Pointer

Classes:
	Ptr<T>							: Shared Pointer
***********************************************************************/

#ifndef VCZH_POINTER
#define VCZH_POINTER

#include "Basic.h"

namespace vl
{

/***********************************************************************
ReferenceCounterOperator
***********************************************************************/

	/// <summary>The strategy to get the pointer to the reference counter from an object. If you get the same pointer multiple times from the same object by calling [M:vl.ReferenceCounterOperator`2.CreateCounter], than it is safe to convert a object pointer to a [T:vl.Ptr`1]. Currently for reflectable C++ types which inherit from [T:vl.reflection.DescriptableObject] it is yet. For others it is no.</summary>
	/// <typeparam name="T">The type of the object.</typeparam>
	/// <typeparam name="Enabled">[T:vl.Ptr`1] will always use [T:vl.YesType] as the second type parameter. This parameter is useful when you want to do partial specialization in the SFINAE way.</typeparam>
	template<typename T, typename Enabled=YesType>
	struct ReferenceCounterOperator
	{
		/// <summary>Create a pointer to the reference counter from an object.</summary>
		/// <returns>The pointer to the reference counter.</returns>
		/// <param name="reference">The object.</param>
		static __forceinline volatile vint* CreateCounter(T* reference)
		{
			return new vint(0);
		}

		/// <summary>Destroy a pointer to the reference counter from an object.</summary>
		/// <param name="counter">The pointer to the reference counter.</param>
		/// <param name="reference">The object.</param>
		static __forceinline void DeleteReference(volatile vint* counter, void* reference)
		{
			delete counter;
			delete (T*)reference;
		}
	};

/***********************************************************************
Ptr
***********************************************************************/

	/// <summary>A smart pointer. It is always safe to convert a pointer to an object to a smart pointer once. If you do it multiple times, it may be wrong due to different implementation of [T:vl.ReferenceCounterOperator`2]. In case of wrong, disposing the smart pointer will cause an access violation.</summary>
	/// <typeparam name="T">The type of the object.</typeparam>
	template<typename T>
	class Ptr
	{
		template<typename X>
		friend class Ptr;
	protected:
		typedef void(*Destructor)(volatile vint*, void*);

		volatile vint*		counter = nullptr;
		T*					reference = nullptr;
		void*				originalReference = nullptr;
		Destructor			originalDestructor = nullptr;

		void SetEmptyNoIncDec()
		{
			counter = nullptr;
			reference = nullptr;
			originalReference = nullptr;
			originalDestructor = nullptr;
		}

		void Inc()
		{
			if (counter)
			{
				INCRC(counter);
			}
		}

		void Dec(bool deleteIfZero = true)
		{
			if (counter)
			{
				if (DECRC(counter) == 0)
				{
					if (deleteIfZero)
					{
						originalDestructor(counter, originalReference);
					}
					SetEmptyNoIncDec();
				}
			}
		}

		volatile vint* Counter()const
		{
			return counter;
		}

		Ptr(volatile vint* _counter, T* _reference, void* _originalReference, Destructor _originalDestructor)
			:counter(_counter)
			, reference(_reference)
			, originalReference(_originalReference)
			, originalDestructor(_originalDestructor)
		{
			Inc();
		}
	public:

		/// <summary>Create a null pointer.</summary>
		Ptr()
		{
		}

		/// <summary>Convert a pointer to an object to a smart pointer.</summary>
		/// <param name="pointer">The pointer to the object.</param>
		Ptr(T* pointer)
		{
			if (pointer)
			{
				counter = ReferenceCounterOperator<T>::CreateCounter(pointer);
				reference = pointer;
				originalReference = pointer;
				originalDestructor = &ReferenceCounterOperator<T>::DeleteReference;
				Inc();
			}
		}

		/// <summary>Copy a smart pointer.</summary>
		/// <param name="pointer">The smart pointer to copy.</param>
		Ptr(const Ptr<T>& pointer)
			:counter(pointer.counter)
			, reference(pointer.reference)
			, originalReference(pointer.originalReference)
			, originalDestructor(pointer.originalDestructor)
		{
			Inc();
		}

		/// <summary>Move a smart pointer.</summary>
		/// <param name="pointer">The smart pointer to Move.</param>
		Ptr(Ptr<T>&& pointer)
			:counter(pointer.counter)
			, reference(pointer.reference)
			, originalReference(pointer.originalReference)
			, originalDestructor(pointer.originalDestructor)
		{
			pointer.SetEmptyNoIncDec();
		}

		/// <summary>Cast a smart pointer.</summary>
		/// <typeparam name="C">The type of the object before casting.</typeparam>
		/// <param name="pointer">The smart pointer to cast.</param>
		template<typename C, typename = typename AcceptType<void, typename PointerConvertable<C, T>::YesNoType>::Type>
		Ptr(const Ptr<C>& pointer)
		{
			if (auto converted = pointer.Obj())
			{
				counter = pointer.Counter();
				reference = converted;
				originalReference = pointer.originalReference;
				originalDestructor = pointer.originalDestructor;
				Inc();
			}
		}

		/// <summary>Cast a smart pointer.</summary>
		/// <typeparam name="C">The type of the object before casting.</typeparam>
		/// <param name="pointer">The smart pointer to cast.</param>
		template<typename C, typename = typename AcceptType<void, typename PointerConvertable<C, T>::YesNoType>::Type>
		Ptr(Ptr<C>&& pointer)
		{
			if (auto converted = pointer.Obj())
			{
				counter = pointer.Counter();
				reference = converted;
				originalReference = pointer.originalReference;
				originalDestructor = pointer.originalDestructor;
				pointer.SetEmptyNoIncDec();
			}
		}

		~Ptr()
		{
			Dec();
		}

		/// <summary>Detach the contained object from this smart pointer.</summary>
		/// <returns>The detached object. Returns null if this smart pointer is empty.</returns>
		T* Detach()
		{
			auto detached = reference;
			Dec(false);
			return detached;
		}

		/// <summary>Cast a smart pointer.</summary>
		/// <typeparam name="C">The type of the object after casting.</typeparam>
		/// <returns>The casted smart pointer. Returns null if failed.</returns>
		template<typename C>
		Ptr<C> Cast()const
		{
			C* converted = dynamic_cast<C*>(reference);
			return Ptr<C>((converted ? counter : 0), converted, originalReference, originalDestructor);
		}

		/// <summary>Convert a pointer to an object to a smart pointer.</summary>
		/// <returns>The converted smart pointer.</returns>
		/// <param name="pointer">The pointer to the object.</param>
		Ptr<T>& operator=(T* pointer)
		{
			Dec();
			if (pointer)
			{
				counter = ReferenceCounterOperator<T>::CreateCounter(pointer);
				reference = pointer;
				originalReference = pointer;
				originalDestructor = &ReferenceCounterOperator<T>::DeleteReference;
				Inc();
			}
			else
			{
				SetEmptyNoIncDec();
			}
			return *this;
		}

		/// <summary>Copy a smart pointer.</summary>
		/// <returns>The copied smart pointer.</returns>
		/// <param name="pointer">The smart pointer to copy.</param>
		Ptr<T>& operator=(const Ptr<T>& pointer)
		{
			if (this != &pointer)
			{
				Dec();
				counter = pointer.counter;
				reference = pointer.reference;
				originalReference = pointer.originalReference;
				originalDestructor = pointer.originalDestructor;
				Inc();
			}
			return *this;
		}

		/// <summary>Move a smart pointer.</summary>
		/// <returns>The moved smart pointer.</returns>
		/// <param name="pointer">The smart pointer to Move.</param>
		Ptr<T>& operator=(Ptr<T>&& pointer)
		{
			if (this != &pointer)
			{
				Dec();
				counter = pointer.counter;
				reference = pointer.reference;
				originalReference = pointer.originalReference;
				originalDestructor = pointer.originalDestructor;
				pointer.SetEmptyNoIncDec();
			}
			return *this;
		}

		bool operator==(const T* pointer)const
		{
			return reference == pointer;
		}

		bool operator!=(const T* pointer)const
		{
			return reference != pointer;
		}

		bool operator>(const T* pointer)const
		{
			return reference > pointer;
		}

		bool operator>=(const T* pointer)const
		{
			return reference >= pointer;
		}

		bool operator<(const T* pointer)const
		{
			return reference < pointer;
		}

		bool operator<=(const T* pointer)const
		{
			return reference <= pointer;
		}

		bool operator==(const Ptr<T>& pointer)const
		{
			return reference == pointer.reference;
		}

		bool operator!=(const Ptr<T>& pointer)const
		{
			return reference != pointer.reference;
		}

		bool operator>(const Ptr<T>& pointer)const
		{
			return reference > pointer.reference;
		}

		bool operator>=(const Ptr<T>& pointer)const
		{
			return reference >= pointer.reference;
		}

		bool operator<(const Ptr<T>& pointer)const
		{
			return reference < pointer.reference;
		}

		bool operator<=(const Ptr<T>& pointer)const
		{
			return reference <= pointer.reference;
		}

		/// <summary>Test if it is a null pointer.</summary>
		/// <returns>Returns true if it is not null.</returns>
		operator bool()const
		{
			return reference != 0;
		}

		/// <summary>Get the pointer to the object.</summary>
		/// <returns>The pointer to the object.</returns>
		T* Obj()const
		{
			return reference;
		}

		/// <summary>Get the pointer to the object.</summary>
		/// <returns>The pointer to the object.</returns>
		T* operator->()const
		{
			return reference;
		}
	};

/***********************************************************************
ComPtr
***********************************************************************/

	template<typename T>
	class ComPtr
	{
	protected:
		volatile vint*		counter;
		T*					reference;

		void Inc()
		{
			if(counter)
			{
				INCRC(counter);
			}
		}

		void Dec()
		{
			if(counter)
			{
				if(DECRC(counter)==0)
				{
					delete counter;
					reference->Release();
					counter=0;
					reference=0;
				}
			}
		}

		volatile vint* Counter()const
		{
			return counter;
		}

		ComPtr(volatile vint* _counter, T* _reference)
			:counter(_counter)
			,reference(_reference)
		{
			Inc();
		}
	public:

		ComPtr()
		{
			counter=0;
			reference=0;
		}

		ComPtr(T* pointer)
		{
			if(pointer)
			{
				counter=new volatile vint(1);
				reference=pointer;
			}
			else
			{
				counter=0;
				reference=0;
			}
		}

		ComPtr(const ComPtr<T>& pointer)
		{
			counter=pointer.counter;
			reference=pointer.reference;
			Inc();
		}

		ComPtr(ComPtr<T>&& pointer)
		{
			counter=pointer.counter;
			reference=pointer.reference;
			
			pointer.counter=0;
			pointer.reference=0;
		}

		~ComPtr()
		{
			Dec();
		}

		ComPtr<T>& operator=(T* pointer)
		{
			Dec();
			if(pointer)
			{
				counter=new vint(1);
				reference=pointer;
			}
			else
			{
				counter=0;
				reference=0;
			}
			return *this;
		}

		ComPtr<T>& operator=(const ComPtr<T>& pointer)
		{
			if(this!=&pointer)
			{
				Dec();
				counter=pointer.counter;
				reference=pointer.reference;
				Inc();
			}
			return *this;
		}

		ComPtr<T>& operator=(ComPtr<T>&& pointer)
		{
			if(this!=&pointer)
			{
				Dec();
				counter=pointer.counter;
				reference=pointer.reference;
				
				pointer.counter=0;
				pointer.reference=0;
			}
			return *this;
		}

		bool operator==(const T* pointer)const
		{
			return reference==pointer;
		}

		bool operator!=(const T* pointer)const
		{
			return reference!=pointer;
		}

		bool operator>(const T* pointer)const
		{
			return reference>pointer;
		}

		bool operator>=(const T* pointer)const
		{
			return reference>=pointer;
		}

		bool operator<(const T* pointer)const
		{
			return reference<pointer;
		}

		bool operator<=(const T* pointer)const
		{
			return reference<=pointer;
		}

		bool operator==(const ComPtr<T>& pointer)const
		{
			return reference==pointer.reference;
		}

		bool operator!=(const ComPtr<T>& pointer)const
		{
			return reference!=pointer.reference;
		}

		bool operator>(const ComPtr<T>& pointer)const
		{
			return reference>pointer.reference;
		}

		bool operator>=(const ComPtr<T>& pointer)const
		{
			return reference>=pointer.reference;
		}

		bool operator<(const ComPtr<T>& pointer)const
		{
			return reference<pointer.reference;
		}

		bool operator<=(const ComPtr<T>& pointer)const
		{
			return reference<=pointer.reference;
		}

		operator bool()const
		{
			return reference!=0;
		}

		T* Obj()const
		{
			return reference;
		}

		T* operator->()const
		{
			return reference;
		}
	};

	template<typename T, typename ...TArgs>
	Ptr<T> MakePtr(TArgs ...args)
	{
		return new T(args...);
	}

/***********************************************************************
Traits
***********************************************************************/

	template<typename T>
	struct KeyType<Ptr<T>>
	{
		typedef T* Type;

		static T* GetKeyValue(const Ptr<T>& key)
		{
			return key.Obj();
		}
	};

	template<typename T>
	struct POD<Ptr<T>>
	{
		static const bool Result=false;
	};

	template<typename T>
	struct KeyType<ComPtr<T>>
	{
		typedef T* Type;

		static T* GetKeyValue(const ComPtr<T>& key)
		{
			return key.Obj();
		}
	};

	template<typename T>
	struct POD<ComPtr<T>>
	{
		static const bool Result=false;
	};
}

#endif